
#include <cegfx/cescene.h>
#include <cegfx/inodeanimator.h>
#include <cegfx/cevirtualclock.h>

ceScene::ceScene ()
	: controller (0)
{
	sectors.clear ();
}


ceScene::~ceScene ()
{
	vector<ceSector*>::iterator it;
	for (it = sectors.begin (); it != sectors.end (); it++)
	{
		ceSector* sector = *it;
		if (sector)
		{
			sector->Release ();
		}
	}

	sectors.clear ();
}

IQF_IMPLEMENTATION_BEGIN(ceScene);
IQF_IMPLEMENTATION_END();


void ceScene::AddSector (ceSector* sector)
{
	if (!sector)
	{
		return;
	}

	sector->AddRef ();
	sectors.push_back (sector);
}


void ceScene::Collect (ceCamera* camera, bool update, ceRenderController* controller)
{
	static long time = ceVClock::GetMilliseconds();
	clipper.Clear();

	SAVE_SET(this->controller, controller);
	controller->Clear();

	// fill the update environment 
	updateEnv.CurrentTime = ceVClock::GetMilliseconds();
	updateEnv.DeltaTime = updateEnv.CurrentTime - time;
	updateEnv.CameraPosition = camera->GetFrom ();


	// build the viewing clipper
	camera->SetupClipper (&clipper);

	vector<ceSector*>::iterator it;
	for (it = sectors.begin (); it != sectors.end (); it++)
	{
		ceSector* sector = *it;
		Collect (/*&clipper*/ 0, update, sector);
	}

	time = updateEnv.CurrentTime;
}

void ceScene::Collect (ceClipper* clipper, bool update, ceNodePtr node)
{
	bool cull = true;
	bool freeCull = false;
	switch (node->GetCullingMode())
	{
	case CM_Cull:
		break;

	case CM_CullNoCullChildren:
		// after testing this node free the clipper so that
		// no children node is testst.
		freeCull = true;
		break;

	case CM_NoCull:
		// don't test anything
		clipper = 0;
		break;

	case CM_NoCullCullChildren:
		// this may appear a bit weired...
		// this node is not testet, but the children nodes are tested.
		cull = false;
		break;

	}

	if (clipper && cull)
	{
		ClippingResult is = clipper->Test (node->GetBoundingBox ());
		switch (is)
		{
		case CR_Out:
			return;
		case CR_In:
			clipper = 0;
			break;
		default:
			break;
		}
	}
	if (freeCull)
	{
		clipper = 0;
	}

	if (update)
	{
		updateEnv.NodeMatrix = node->GetTransform ().GetMatrix();
		node->Update (updateEnv);
	}


	switch (node->GetNodeType ())
	{
	case ceNode::NT_Collection:
		{
			// iterate over all the nodes within the collection
			ceNodeCollection* col = static_cast<ceNodeCollection*>(node);
			for (unsigned i=0, j=col->GetNumberOfNodes (); i<j; i++)
			{
				Collect (clipper, update, col->GetNode (i));
			}
		}
		break;

	case ceNode::NT_GeometryNode:
		{
			D3DXVECTOR3 diff = node->GetTransform().GetPosition () - updateEnv.CameraPosition;
			float squaredDistance = D3DXVec3Dot (&diff, &diff);
			
			if (node->GetRenderingRange ().IsInRange (squaredDistance))
			{
				ceGeometryNode* geom = static_cast<ceGeometryNode*>(node);
				controller->AddGeometryNode (geom);
			}
		}
		break;

	case ceNode::NT_LightNode:
		// hmm... not knowing how to handle this propper
		break;
	}

}


void ceScene::Render (ceRenderController* controller, const iViewport* viewport)
{
	controller->Render(0, &clipper);

}
